/* simple_tcp_server.c
 * 作用：监听本机指定端口，接收客户端(127.0.0.1)发来的消息并打印
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/* 必须与客户端的 SERVPORT 一致 */
#define SERVPORT  18888           /* 改成你自己在客户端里定义的端口 */
#define BACKLOG   5              /* 同时挂起的最大连接数   */
#define BUFSIZE   1024

int main (void)
{
    int listenfd, connfd;                /* 监听/已连接套接字         */
    struct sockaddr_in serv_addr;        /* 服务器地址结构体           */
    struct sockaddr_in cli_addr;         /* 客户端地址结构体           */
    socklen_t cli_len;
    char buf[BUFSIZE];
    int ret;

    /* 1. 创建监听套接字 */
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    /* 2. 允许端口快速复用(调试时很有用，不是必须) */
    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    /* 3. 绑定到本机(0.0.0.0 => 任意网卡) + 指定端口 */
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family      = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);  /* 0.0.0.0 */
    serv_addr.sin_port        = htons(SERVPORT);

    if (bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
        perror("bind");
        close(listenfd);
        exit(EXIT_FAILURE);
    }

    /* 4. 进入监听状态 */
    if (listen(listenfd, BACKLOG) == -1) {
        perror("listen");
        close(listenfd);
        exit(EXIT_FAILURE);
    }
    printf("Server started : 0.0.0.0:%d \n", SERVPORT);

    /* 5. 主循环：阻塞等待并处理连接 */
    while (1) {
        cli_len = sizeof(cli_addr);
        connfd  = accept(listenfd, (struct sockaddr *)&cli_addr, &cli_len);
        if (connfd < 0) {
            perror("accept");
            continue;                       /* 继续接收下一条连接        */
        }

        char cli_ip[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &cli_addr.sin_addr, cli_ip, sizeof(cli_ip));
        printf("\n>> New connection from %s:%d\n",
               cli_ip, ntohs(cli_addr.sin_port));

        /* 6. 循环收数据直到对端关闭 */
        while ((ret = recv(connfd, buf, sizeof(buf) - 1, 0)) > 0) {
            buf[ret] = '\0';               /* recv 不会自动加 '\0'      */
            printf("   Received: %s\n", buf);
        }

        if (ret == 0) {
            printf("<< Client %s:%d closed the connection\n",
                   cli_ip, ntohs(cli_addr.sin_port));
        } else if (ret < 0) {
            perror("recv");
        }

        close(connfd);                      /* 关闭本次连接，回到 accept  */
    }

    /* 不会走到这，但按习惯写上 */
    close(listenfd);
    return 0;
}